package com.swak.vertx.protocol.http;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import com.swak.Constants;
import com.swak.annotation.Body;
import com.swak.annotation.Cookie;
import com.swak.annotation.Header;
import com.swak.annotation.Json;
import com.swak.asm.FieldCache;
import com.swak.asm.FieldCache.ClassMeta;
import com.swak.asm.FieldCache.FieldMeta;
import com.swak.entity.UploadFile;
import com.swak.exception.ValidatorException;
import com.swak.security.Platform;
import com.swak.security.Subject;
import com.swak.utils.Converters;
import com.swak.utils.JsonMapper;
import com.swak.utils.Lists;
import com.swak.utils.Maps;
import com.swak.utils.StringUtils;
import com.swak.validator.Validator;
import com.swak.validator.errors.BindErrors;
import com.swak.vertx.invoker.MethodInvoker;
import com.swak.vertx.invoker.MethodInvoker.MethodParameter;
import com.swak.vertx.remote.IP;
import com.swak.vertx.security.SecuritySubject;

import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.RoutingContext;

/**
 * 参数解析：
 * 
 * 默认能处理的参数格式是：1.  Content-type : application/x-www-form-urlencoded 但是此格式 服务器是不支持 get。
 * 
 * 所以对于get请求需要转换为：2.  Content-type :application/json。
 * 
 * 服务器参数的处理： 服务器对于1.的类型是可以默认处理的，需要配合qs（转换下）为啥要转换？
 * 
 * 对于2.的类型的参数需要手动处理。
 * 
 * @author DELL
 */
public class ParamParser {

	protected static Logger logger = LoggerFactory.getLogger(ParamParser.class);
	private Validator validator;

	public ParamParser(Validator validator) {
		this.validator = validator;
	}

	/**
	 * 解析并验证参数
	 */
	public Object[] parseParameters(RoutingContext context, MethodInvoker method) {

		/*
		 * 打印获取到的参数
		 */
		if (logger.isDebugEnabled()) {
			logger.debug("headers:{},\n body:{}, \n params:{}", context.request().headers(), context.body().asString(),
					context.request().params());
		}

		// 解析参数
		MethodParameter[] parameters = method.getParameters();
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			args[i] = this.parseParameter(parameter, context);
		}

		// 验证参数
		if (this.validator != null && context.data().containsKey(Constants.VALIDATE_NAME)
				&& this.getBindErrors(context).hasError()
				&& !context.data().containsKey(Constants.VALIDATE_Processed_NAME)) {
			throw new ValidatorException(this.getBindErrors(context));
		}
		return args;
	}

	/**
	 * 支持的参数解析(方便测试)
	 */
	protected Object parseParameter(MethodParameter parameter, RoutingContext context) {
		Class<?> parameterType = parameter.getParameterType();
		if (HttpServerRequest.class.isAssignableFrom(parameterType)) {
			return context.request();
		} else if (HttpServerResponse.class.isAssignableFrom(parameterType)) {
			return context.response();
		} else if (RoutingContext.class.isAssignableFrom(parameterType)) {
			return context;
		} else if (IP.class.isAssignableFrom(parameterType)) {
			return IP.parse(context);
		} else if (Platform.class.isAssignableFrom(parameterType)) {
			Platform app = new Platform();
			app.setAppId(context.request().getHeader(Constants.App_NAME));
			app.setOpenId(context.request().getHeader(Constants.Open_NAME));
			app.setPlatform(context.request().getHeader(Constants.Platform_NAME));
			return app;
		} else if (Subject.class.isAssignableFrom(parameterType)) {
			Subject subject = context.get(Constants.SUBJECT_NAME);
			if (subject == null) {
				subject = new SecuritySubject();
				context.put(Constants.SUBJECT_NAME, subject);
			}
			return subject;
		} else if (BindErrors.class.isAssignableFrom(parameterType)) {
			return this.manualProcessBindErrors(context);
		} else if (parameter.hasConvertAnnotation()) {
			return this.resolveAnnotation(parameter, context);
		} else if (BeanUtils.isSimpleProperty(parameterType)) {
			return this.doConvert(context.request().getParam(parameter.getParameterName()), parameterType);
		} else if (List.class.isAssignableFrom(parameterType)) {
			String resolvedName = parameter.getParameterName();
			return context.request().params().getAll(resolvedName);
		} else if (Map.class.isAssignableFrom(parameterType)) {
			return this.parseArguments(context.request().params().iterator());
		} else if (UploadFile.class.isAssignableFrom(parameterType)) {
			UploadFile file = new UploadFile();
			List<FileUpload> fileUploads = context.fileUploads();
			if (!Lists.isEmpty(fileUploads)) {
				FileUpload upload = fileUploads.get(0);
				file.setName(upload.fileName());
				file.setStorage(upload.uploadedFileName());
			}
			return file;
		}
		return this.resolveObjectAndValidate(parameter, context);
	}

	/**
	 * 解析注解
	 */
	protected Object resolveAnnotation(MethodParameter parameter, RoutingContext context) {
		Body body = parameter.getBodyAnnotation();
		if (body != null) {
			if (!parameter.getParameterType().isArray() && BeanUtils.isSimpleProperty(parameter.getParameterType())) {
				return this.doConvert(context.body().asString(), parameter.getParameterType());
			}
			return context.body().buffer().getBytes();
		}
		Json json = parameter.getJsonAnnotation();
		if (json != null) {
			Class<?> fieldClass = parameter.getParameterType();
			if (List.class.isAssignableFrom(fieldClass)) {
				return JsonMapper.fromJsonToList(context.request().getParam(parameter.getParameterName()),
						parameter.getNestedParameterType());
			} else if (Map.class.isAssignableFrom(fieldClass)) {
				return JsonMapper.fromJson(context.request().getParam(parameter.getParameterName()), HashMap.class);
			}
			return JsonMapper.fromJson(context.request().getParam(parameter.getParameterName()), fieldClass);
		}
		Header header = parameter.getHeaderAnnotation();
		if (header != null) {
			Class<?> fieldClass = parameter.getParameterType();
			if (Map.class.isAssignableFrom(fieldClass)) {
				return this.parseHeaders(context);
			} else if (List.class.isAssignableFrom(fieldClass)) {
				return context.request().headers().getAll(parameter.getParameterName());
			}
			return this.doConvert(context.request().getHeader(parameter.getParameterName()),
					parameter.getParameterType());
		}
		Cookie cookie = parameter.getCookieAnnotation();
		if (cookie != null) {
			String cookieName = StringUtils.isBlank(cookie.value()) ? parameter.getParameterName() : cookie.value();
			io.vertx.core.http.Cookie _cookie = context.request().getCookie(cookieName);
			if (_cookie != null) {
				return this.doConvert(_cookie.getValue(), parameter.getParameterType());
			}
			String value = context.request().getParam(parameter.getParameterName());
			return this.doConvert(value, parameter.getParameterType());
		}
		return null;
	}

	/**
	 * 解析参数并验证
	 */
	protected Object resolveObjectAndValidate(MethodParameter parameter, RoutingContext context) {

		// 需要验证
		if (validator != null && parameter.getValidAnnotation() != null) {
			return this.resolveObject(parameter, context, true);
		}

		// 不需要验证
		return this.resolveObject(parameter, context, false);
	}

	/**
	 * 手动处理验证异常 ： 标注手动处理异常
	 */
	protected BindErrors manualProcessBindErrors(RoutingContext context) {
		BindErrors errors = context.get(Constants.VALIDATE_NAME);
		if (errors == null) {
			errors = BindErrors.of(Maps.newHashMap());
			context.put(Constants.VALIDATE_NAME, errors);
		}
		context.put(Constants.VALIDATE_Processed_NAME, true);
		return errors;
	}

	/**
	 * 初始化绑定错误
	 */
	protected BindErrors getBindErrors(RoutingContext context) {
		BindErrors errors = context.get(Constants.VALIDATE_NAME);
		if (errors == null) {
			errors = BindErrors.of(Maps.newHashMap());
			context.put(Constants.VALIDATE_NAME, errors);
		}
		return errors;
	}

	/**
	 * 直接解析对象参数 （只填充第一层, 第二层）
	 */
	protected Object resolveObject(MethodParameter parameter, RoutingContext context, boolean check) {
		Map<String, Object> arguments = this.parseArguments(context.request().params().iterator());
		return this.resolveObject(parameter.getParameterType(), parameter.getParameterName(), arguments, context,
				check);
	}

	/**
	 * 解析参数: 缓存默认的构造函数
	 * 
	 * @param clazz     需转换的类型
	 * @param pname     参数名称
	 * @param arguments 具体的参数
	 * @param context   上下文
	 * @param check     是否校验
	 * @return 解析后的参数
	 */
	public Object resolveObject(Class<?> clazz, String pname, Map<String, Object> arguments, RoutingContext context,
			boolean check) {
		Object obj = null;
		try {
			ClassMeta classMeta = FieldCache.get(clazz);
			obj = classMeta.getDefaultConstructor().newInstance();
			// obj = clazz.getDeclaredConstructor();
			if (classMeta != null && (!arguments.isEmpty() || check)) {
				this.fillObjectValue(obj, classMeta.getFields(), pname, arguments, context, check);
			}
		} catch (Exception e) {
			logger.error("Set field faile");
		}
		return obj;
	}

	@SuppressWarnings("unchecked")
	protected void fillObjectValue(Object obj, Map<String, FieldMeta> fields, String paramName,
			Map<String, Object> arguments, RoutingContext context, boolean check) throws IllegalArgumentException {

		// 优先使用下一级的数据
		Object values = arguments;
		if (arguments.containsKey(paramName)) {
			values = arguments.get(paramName);
		}
		if (!(values instanceof Map)) {
			values = arguments;
		}

		// 前端传递的数据
		Map<String, Object> maps = ((Map<String, Object>) values);

		// 添加一层空值(保证每一个校验都生效)
		if (check) {
			fields.keySet().forEach(key -> {
				if (!maps.containsKey(key)) {
					maps.put(key, null);
				}
			});
		}

		// 循环设置数据
		Iterator<Map.Entry<String, Object>> entrys = maps.entrySet().iterator();
		while (entrys.hasNext()) {

			// 基础数据
			Map.Entry<String, Object> entry = entrys.next();
			String key = entry.getKey();
			Object value = entry.getValue();

			// 是否可以获取配置
			FieldMeta field = fields.get(key);

			// 不存在的字段，不需要处理
			if (field == null) {
				continue;
			}

			// 设置值
			try {
				// 处理结果
				Object result = null;

				// 值为空
				if (value == null || StringUtils.NULL.equals(value)) {
					result = null;
				}
				// 值不为空
				else {
					if (field.hasAnnotation(Json.class)) {
						if (value instanceof String) {
							result = this.doJsonMapper(value, field);
						} else {
							result = this.doConvert(value, field.getFieldClass());
						}
					} else if (List.class.isAssignableFrom(field.getFieldClass())) {
						if (value instanceof List) {
							result = this.doConvert(value, field.getFieldClass());
						} else if (value instanceof String) {
							result = this.doConvert(Lists.newArrayList(value), field.getFieldClass());
						} else if (value instanceof Map && BeanUtils.isSimpleProperty(field.getNestedFieldClass())) {
							result = this.doConvert(((Map<String, Object>) value).values(), field.getFieldClass());
						} else if (value instanceof Map && Map.class.isAssignableFrom(field.getNestedFieldClass())) {
							result = this.doConvert(((Map<String, Object>) value).values(), field.getFieldClass());
						} else if (value instanceof Map) {
							result = this.resolveChildObject(field.getNestedFieldClass(), (Map<String, Object>) value,
									context, check);
						}
					} else if (Map.class.isAssignableFrom(field.getFieldClass())) {
						if (value instanceof Map) {
							result = this.doConvert(value, field.getFieldClass());
						} else {
							result = Maps.newHashMap();
						}
					} else if (BeanUtils.isSimpleProperty(field.getFieldClass())) {
						result = this.doConvert(value, field.getFieldClass());
					} else if (value instanceof Map) {
						result = this.resolveObject(field.getFieldClass(), field.getPropertyName(),
								((Map<String, Object>) values), context, check);
					}

					// 设置值
					// field.getField().set(obj, result);
					this.setFieldValue(field, obj, result);
				}

				// 需要校验, 存储校验结构
				String error;
				if (check && (error = this.validator.validate(field, result)) != null) {
					BindErrors errors = this.getBindErrors(context);
					errors.addError(paramName, field.getPropertyName(), error);
				}
			} catch (Exception e) {
				logger.error("Request {}, Set Object[{}] field [{}] faile : value [{}] ", context.request().uri(),
						obj.getClass().getName(), field.getPropertyName(), value);
			}
		}
	}

	/**
	 * 优化：设置 属性的值
	 * 
	 * 1. 默认通过反射注入值；
	 * 2. 可以生成对应 set 方法的 lamda 表达式
	 * 
	 * @param field
	 * @param obj
	 * @param result
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	private void setFieldValue(FieldMeta field, Object obj, Object result)
			throws IllegalArgumentException, IllegalAccessException {
		// field.getField().set(obj, result);
		field.setFieldValue(obj, result);
	}

	/**
	 * list 子类型的解析
	 */
	@SuppressWarnings("unchecked")
	protected Object resolveChildObject(Class<?> clazz, Map<String, Object> arguments, RoutingContext context,
			boolean check) {
		List<Object> values = Lists.newArrayList();
		arguments.forEach((key, value) -> {
			if (value instanceof Map) {
				value = this.resolveObject(clazz, StringUtils.EMPTY, (Map<String, Object>) value, context, check);
				values.add(value);
			}
		});
		return values;
	}

	protected Map<String, Object> parseHeaders(RoutingContext request) {
		MultiMap maps = request.request().headers();
		Map<String, Object> arguments = new LinkedHashMap<>();
		maps.forEach(entry -> arguments.put(entry.getKey(), entry.getValue()));
		return arguments;
	}

	/**
	 * 解析参数
	 */
	public Map<String, Object> parseArguments(Iterator<Map.Entry<String, String>> entrys) {
		Map<String, Object> arguments = new LinkedHashMap<>();
		while (entrys.hasNext()) {
			Map.Entry<String, String> entry = entrys.next();
			String key = entry.getKey();
			if (logger.isDebugEnabled()) {
				logger.debug("param: {}", key);
			}
			String k2 = null;
			String k3 = null;
			String k4 = null;
			String k5 = null;
			String k6 = null;
			String k7 = null;
			String k8 = null;
			String k9 = null;
			String k10 = null;
			if (StringUtils.contains(key, "[")) {
				String[] values = key.split("\\[");
				key = values[0];
				k2 = values.length >= 2 ? values[1].substring(0, values[1].length() - 1) : null;
				k3 = values.length >= 3 ? values[2].substring(0, values[2].length() - 1) : null;
				k4 = values.length >= 4 ? values[3].substring(0, values[3].length() - 1) : null;
				k5 = values.length >= 5 ? values[4].substring(0, values[4].length() - 1) : null;
				k6 = values.length >= 6 ? values[5].substring(0, values[5].length() - 1) : null;
				k7 = values.length >= 7 ? values[6].substring(0, values[6].length() - 1) : null;
				k8 = values.length >= 8 ? values[7].substring(0, values[7].length() - 1) : null;
				k9 = values.length >= 9 ? values[8].substring(0, values[8].length() - 1) : null;
				k10 = values.length >= 10 ? values[9].substring(0, values[9].length() - 1) : null;
			}
			// 十层数据设置
			if (k2 != null && k3 != null && k4 != null && k5 != null && k6 != null && k7 != null && k8 != null
					&& k9 != null && k10 != null) {
				this.processArgs10(arguments, entry, key, k2, k3, k4, k5, k6, k7, k8, k9, k10);
			}
			// 九层数据设置
			else if (k2 != null && k3 != null && k4 != null && k5 != null && k6 != null && k7 != null && k8 != null
					&& k9 != null) {
				this.processArgs9(arguments, entry, key, k2, k3, k4, k5, k6, k7, k8, k9);
			}
			// 八层数据设置
			else if (k2 != null && k3 != null && k4 != null && k5 != null && k6 != null && k7 != null && k8 != null) {
				this.processArgs8(arguments, entry, key, k2, k3, k4, k5, k6, k7, k8);
			}
			// 七层数据设置
			else if (k2 != null && k3 != null && k4 != null && k5 != null && k6 != null && k7 != null) {
				this.processArgs7(arguments, entry, key, k2, k3, k4, k5, k6, k7);
			}
			// 六层数据设置
			else if (k2 != null && k3 != null && k4 != null && k5 != null && k6 != null) {
				this.processArgs6(arguments, entry, key, k2, k3, k4, k5, k6);
			}
			// 五层数据设置
			else if (k2 != null && k3 != null && k4 != null && k5 != null) {
				this.processArgs5(arguments, entry, key, k2, k3, k4, k5);
			}
			// 四层数据设置
			else if (k2 != null && k3 != null && k4 != null) {
				this.processArgs4(arguments, entry, key, k2, k3, k4);
			}
			// 三层数据设置
			else if (k2 != null && k3 != null) {
				this.processArgs3(arguments, entry, key, k2, k3);
			}
			// 两层数据设置
			else if (k2 != null) {
				this.processArgs2(arguments, entry, key, k2);
			}
			// 一层数据设置, 可以有多个值
			else {
				this.addMap(arguments, key, entry.getValue());
			}
		}
		return arguments;
	}

	@SuppressWarnings("unchecked")
	protected void processArgs10(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3, String k4, String k5, String k6, String k7, String k8, String k9, String k10) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs9(kValues, entry, k2, k3, k4, k5, k6, k7, k8, k9, k10);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs9(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3, String k4, String k5, String k6, String k7, String k8, String k9) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs8(kValues, entry, k2, k3, k4, k5, k6, k7, k8, k9);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs8(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3, String k4, String k5, String k6, String k7, String k8) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs7(kValues, entry, k2, k3, k4, k5, k6, k7, k8);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs7(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3, String k4, String k5, String k6, String k7) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs6(kValues, entry, k2, k3, k4, k5, k6, k7);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs6(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3, String k4, String k5, String k6) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs5(kValues, entry, k2, k3, k4, k5, k6);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs5(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3, String k4, String k5) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs4(kValues, entry, k2, k3, k4, k5);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs4(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3, String k4) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs3(kValues, entry, k2, k3, k4);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs3(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2,
			String k3) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.processArgs2(kValues, entry, k2, k3);
	}

	@SuppressWarnings("unchecked")
	protected void processArgs2(Map<String, Object> arguments, Map.Entry<String, String> entry, String key, String k2) {
		Map<String, Object> kValues = (Map<String, Object>) arguments.get(key);
		if (kValues == null) {
			kValues = Maps.newOrderMap();
			arguments.put(key, kValues);
		}
		this.addMap(kValues, k2, entry.getValue());
	}

	/**
	 * 相同的值逐渐升级为 LIST
	 */
	@SuppressWarnings("unchecked")
	protected void addMap(Map<String, Object> arguments, String key, Object value) {
		if (arguments.containsKey(key) && arguments.get(key) instanceof List) {
			List<Object> values = ((List<Object>) arguments.get(key));
			values.add(value);
		} else if (arguments.containsKey(key) && arguments.get(key) instanceof String) {
			List<Object> values = Lists.newArrayList();
			values.add(arguments.get(key));
			values.add(value);
			arguments.put(key, values);
		} else {
			arguments.put(key, value);
		}
	}

	/**
	 * 执行JSON转换
	 */
	protected Object doJsonMapper(Object json, FieldMeta field) {
		Class<?> fieldClass = field.getFieldClass();
		if (List.class.isAssignableFrom(fieldClass)) {
			return JsonMapper.fromJsonToList(json.toString(), field.getNestedFieldClass());
		} else if (Map.class.isAssignableFrom(fieldClass)) {
			return JsonMapper.fromJson(json.toString(), HashMap.class);
		}
		return JsonMapper.fromJson(json.toString(), field.getFieldClass());
	}

	/**
	 * 执行转换
	 */
	protected Object doConvert(Object value, Class<?> targetType) {
//		TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(value);
//		TypeDescriptor targetDescriptor = TypeDescriptor.valueOf(targetType);
//		if (Converters..canConvert(sourceTypeDesc, targetDescriptor)) {
//			return conversionService.convert(value, sourceTypeDesc, targetDescriptor);
//		}
		return Converters.doConvert(value, targetType);
	}
}
